%{
    int yyparse(void);
    int yylex(void);
    int yywrap(){
        return 1;
    }

#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include "semantics.h"
#include "calculator.h"

extern int lineNumber;
const list* fList = NULL;
const list* args = NULL;
const list* vRoot = NULL;
%}

%union {
    const char* lexeme;      //identifier
    char charType;
    double value;            //value of an identifier of type NUM
    const list* vars;
    const var* VAR;
    bool boolean;
}

/* Note: the further down the declaration is the higher the precedence is */

%right blockStart listStart
%left blockEnd listEnd
%token stmtEnd
%token selector listSeparator questionMark
%token IF ELSE SWITCH CASE DEFAULT DO WHILE FOR CONTINUE BREAK CONST TRUE FALSE
%right RETURN
%token CHAR INT FLOAT VOID STRING BOOL


%token <lexeme> identifier
%token <lexeme> stringValue
%token <charType> charValue
%token <value> integer floatNumber
%left greater greaterEqual less lessEqual equal notEqual
%left OR
%left AND
%left NOT
%right assignment
%left plus minus
%left multiplication division modulo
%left NEG POS
%nonassoc increment decrement

%nonassoc IFX
%nonassoc ELSE

%type <lexeme> type typeQualifier
%type <vars> arguments parameterList stmts variableList extendedVariableList variableDeclaration instruction largeStmtBlock
%type <VAR> value operator assign jump methodCall operation
%type <boolean> comparison expression
%start globalUnit

%%

/* this allows a mix of functions and variable declarations in the global scope */
globalUnit : globalDeclaration  {printf("single Declaration\n");}
           | globalUnit globalDeclaration  {printf("multiple declaration\n");};

globalDeclaration : functionDeclaration {printf("Function\n");}
                  | variableDeclaration  {printf("VARIABLE\n");}
                  | assign stmtEnd {printf("global Assignment\n");}
                  | stmtEnd {printf("empty instruction\n");};

functionDeclaration : type identifier parameterList largeStmtBlock {assert($1); fList = addFunction(fList,$1,$3,$2,$4); /*list_remove(&args);*/ args=NULL;  /*list_remove(&vRoot);*/ vRoot = NULL; printf("function declaration"); printAll(args, vRoot, NULL);};

parameterList : listStart arguments listEnd  {assert(!args); assert(!vRoot); assert($2); $$ = $2; args = $2; assert($2); assert(args); assert(!vRoot); vRoot = mergeVars(args, vRoot); printf("parameter list\n"); printAll(args,vRoot, $$); }
              | listStart listEnd  {$$ = NULL; printf("empty param list\n"); printAll(args, vRoot, $$);}
              | listStart VOID listEnd {$$ = NULL; printf("void list\n");};

arguments : type identifier  {$$ = NULL; assert(!vRoot); assert(!args); $$ = addVarWithType($1,$$, $2);  assert($$); assert(!vRoot); assert(!args); assert(!vRoot || vRoot->next!=args); printf("arguments\n"); printAll(args, vRoot, $$);}
          | arguments listSeparator type identifier  {assert($1); assert(!vRoot); assert(!args); assert($$); assert($$==$1); $$ = addVarWithType($3, $$, $4);  assert(!args); assert(!vRoot || vRoot->next!=args); printf("multiple arguments\n"); printAll(args, vRoot, $$);};

/* if, else, for can also have only a single instruction that has to be executed,
   while functions are always enclosed by a block (=> largeStmtBlock).*/
stmtBlock : instruction {printf("single instruction\n");}
          | largeStmtBlock {printf("large stmt block\n");};

largeStmtBlock : blockStart stmts blockEnd {$$ = vRoot; printf("\n\nvariable list:\n"); printList(vRoot, true); printf("stmt block\n"); printAll(args, vRoot, $$);}
               | blockStart blockEnd {$$ = NULL; printf("empty statement block\n"); printAll(args, vRoot, $$);};

stmts : instruction {printf("one statement\n"); printAll(args, vRoot, NULL);}
      | stmts instruction {printf("statements");  printAll(args, vRoot, NULL);};

instruction : variableDeclaration {printf("instruction - var declaration"); printAll(args, vRoot, NULL); }
            | assign stmtEnd {assert($1 && $1->name); checkAssignment(vRoot, $1); printf("assign\n"); printAll2(args, vRoot, $1);}
            | operation stmtEnd {printf("operation instruction\n");}
            | selection {printf("selection if etc\n");}
            | loop {printf("loop\n");}
            | jump {printf("jump\n"); printAll2(args, vRoot, $1);}
            | methodCall {printf("method call\n");}
            | stmtEnd {printf("empty instruction\n");};

/* TYPES and TypeQualifiers */
typeQualifier : CONST  {$$ = "const";};

type : CHAR {$$ = "char";}
     | INT {$$ = "int";}
     | FLOAT {$$ = "float";}
     | STRING {$$ = "string";}
     | VOID {$$ = "void";}
     | BOOL {$$ = "bool";};


/* VARIABLE DECLARATIONS (LIST of variable names of the same type is possible) */
variableDeclaration : typeQualifier type extendedVariableList stmtEnd {assert($2); setType1($3, $2, $1); vRoot = mergeVars(vRoot, $3); printf("Variable decl 1\n"); printAll(args, vRoot, NULL);}
                    | type extendedVariableList stmtEnd {assert($2); setType($2, $1); printList($2,true); printAll(args,vRoot,NULL);  vRoot = mergeVars(vRoot, $2); printf("Variable decl 2\n"); printAll(args, vRoot, NULL);};

variableList : operator {$$ = NULL; $$ = addVar1($$,$1); printf("var list - operator\n"); printAll(args, vRoot, $$); }
             | variableList listSeparator operator {$$ = addVar1($$,$3); printf("multiple var list\n");};

extendedVariableList : identifier {/* has no sense: assert(!vRoot);*/ $$ = addVar(NULL, NULL, $1); printf("extended var list identifier\n"); printAll(args, vRoot, $$);}
                     | assign {$$ = addVar1(NULL, $1); printf("extended var list assignment\n"); printAll(args, vRoot, $$);}
                     | extendedVariableList listSeparator assign {$$ = addVar1($$, $3); printf("extended var list multiple assignment\n"); printAll(args, vRoot, $$);}
                     | extendedVariableList listSeparator identifier {$$ = addVar(NULL,$$, $3); printf("extended var list multiple identifier\n"); printAll(args, vRoot, $$);};

methodCall : identifier listStart variableList listEnd stmtEnd {findFunct(fList, $1, $3); $$ = NULL; printf("method called\n");}
           | identifier listStart listEnd stmtEnd {findFunct(fList, $1, NULL); $$ = NULL; printf("method called - empty parameter list\n");};

jump : CONTINUE stmtEnd {printf("continue\n");}
     | BREAK stmtEnd {printf("break\n");}
     | RETURN stmtEnd {var* v = newVarWithoutValue(NULL, NULL, "const"); v->returned = true; vRoot = addVar1(vRoot, v); $$ = v; printf("return without return value\n"); }
     | RETURN expression stmtEnd {var* v = newVarBool(NULL, $2); v->returned = true; $$ = v; vRoot = addVar1(vRoot, v); assert($$); printf("return with return value\n"); printAll2(args, vRoot,v);};
     | RETURN operator stmtEnd {
                            var* v = (var*) $2;
                            if(v->name){
                                v = findVar(v->name, vRoot);
                                assert(v);
                            }
                            else vRoot = addVar1(vRoot, v);
                            v->returned = true; $$ = v; assert($$); printf("return operator\n"); printAll2(args, vRoot, $$);
                            };

loop : WHILE listStart expression listEnd stmtBlock {printf("while\n");}
     | DO stmtBlock WHILE listStart expression listEnd stmtEnd {printf("do while\n");}
     | FOR listStart stmtEnd stmtEnd listEnd stmtBlock {printf("for\n");}
     | FOR listStart stmtEnd stmtEnd expression listEnd stmtBlock {printf("for\n");}
     | FOR listStart stmtEnd expression stmtEnd listEnd stmtBlock {printf("for\n");}
     | FOR listStart stmtEnd expression stmtEnd expression listEnd stmtBlock {printf("for\n");}
     | FOR listStart expression stmtEnd stmtEnd listEnd stmtBlock {printf("for\n");}
     | FOR listStart expression stmtEnd stmtEnd expression listEnd stmtBlock {printf("for\n");}
     | FOR listStart expression stmtEnd expression stmtEnd listEnd stmtBlock {printf("for\n");}
     | FOR listStart expression stmtEnd expression stmtEnd expression listEnd stmtBlock {printf("for\n");};

selection : IF listStart expression listEnd stmtBlock %prec IFX {printf("if \n");}
          | IF listStart expression listEnd stmtBlock ELSE stmtBlock {printf("if-else\n");}
          | SWITCH listStart expression listEnd stmtBlock {printf("switch\n");};

expression : expression AND expression {$$ = $1 && $3;printf("AND\n");}
           | expression OR expression {$$ = $1 || $3; printf("OR\n");}
           | NOT expression {$$ = !$2; printf("NOT\n");}
           | listStart expression listEnd {$$ = $2; printf("( expression list )\n");}
           | comparison {printf("compare\n");};
           

comparison : operator greater operator {$$ = varGreater($1,$3); printf("greater\n");}
           | operator greaterEqual operator {$$ = !varLess($1,$3); printf("greaterEqual\n");}
           | operator less operator {$$ = varLess($1,$3); printf("less\n");}
           | operator lessEqual operator {$$ = !varGreater($1,$3); printf("lessEqual\n");}
           | operator equal operator {$$ = varEquals($1,$3); printf("equal\n");}
           | operator notEqual operator {$$ = !varEquals($1,$3); printf("notEqual\n");};

assign : identifier assignment operator   {$$ = findOrCreateVar($1, vRoot); assert($$ && $3); $$ = assign($$, $3); printf("assignment\n"); printAll2(args, vRoot, $$);}
       | identifier assignment expression {$$ = findOrCreateVar($1, vRoot); assert($$); $$ = assign($$, newVarBool(NULL,$3)); printf("assignment 2\n");}
       | identifier assignment assign     {printf("multiple assignment\n"); printAll2(args, vRoot, $$);}
       | identifier assignment methodCall {printf("method call assignment\n");};

operation : operator multiplication operator {$$ = multiply($1, $3); printf("multiplication\n");}
          | operator plus operator {$$ = sum($1, $3); printf("addition\n");}
          | minus operator %prec NEG {$$ = negate($2); printf("negation\n");}
          | plus operator %prec POS {$$ = $2; printf("positive\n");}
          | operator minus operator {$$ = subtract($1, $3); printf("subtraction\n");}
          | operator division operator {$$ = divide($1, $3); printf("division\n");}
          | operator modulo operator {$$ = mod($1, $3); printf("modulo\n");}
          | identifier increment {$$ = findVar($1, vRoot); union value v; v.i =  1; var * tmp = newVarWithValue("int", NULL, v); $$ = sum($$,tmp);  printf("increment\n");}
          | identifier decrement {$$ = findVar($1, vRoot); union value v; v.i = -1; var * tmp = newVarWithValue("int", NULL, v); $$ = sum($$,tmp); printf("decrement\n");}
          | increment identifier {var * u = findVar($2, vRoot); union value v; v.i =  1; var * tmp = newVarWithValue("int", NULL, v);  $$ = sum(u,tmp); printf("increment\n");}
          | decrement identifier {var * u = findVar($2, vRoot); union value v; v.i = -1; var * tmp = newVarWithValue("int", NULL, v);  $$ = sum(u,tmp); printf("decrement\n");}
          | listStart operation listEnd {$$ = $2; printf("operation block\n");};

operator: identifier {$$ = findVar($1, vRoot); printf("identifier\n"); printAll(args, vRoot, NULL);}
        | value {printf("value\n"); printAll2(args, vRoot, $$);};

value : operation   {printf("value return by operation\n");}
      | integer     {$$ = newVarInt(NULL, $1);   printf("integer Value\n"); printAll2(args, vRoot, $$);}
      | floatNumber {$$ = newVarFloat(NULL, $1); printf("float Value\n"); printAll2(args, vRoot, $$);}
      | stringValue {$$ = newVarString(NULL, $1); printf("string Value\n"); printAll2(args, vRoot, $$);}
      | charValue   {$$ = newVarChar(NULL, $1);    printf("character value\n"); printAll2(args, vRoot, $$);}
      | TRUE {$$ = newVarBool(NULL, true); printf("true\n");}
      | FALSE {$$ = newVarBool(NULL, false); printf("false\n");};
     
%%

#include "lex.yy.c"

int main(void){
//    extern int yydebug;
//    yydebug = 1;
    yyparse();
    printList(fList, false);
    
    
//    if(!findNode(fList, "main", false)){
//        yyerror("no \"main\" function found");
//    }
    
    return 0;
}
